fork 和 exec 的区别
使用fork并exec体现了 UNIX 的精神,因为它提供了一种非常简单的方式来启动新进程。
fork
exec
该fork调用基本上复制了当前进程,几乎在所有方面都相同。并非所有内容都被复制(例如,某些实现中的资源限制),但其想法是创建尽可能接近的副本。
新进程(子进程)获得不同的进程 ID(PID),并将旧进程(父进程)的 PID 作为其父进程 PID(PPID)。因为这两个进程现在运行的代码完全相同,所以它们可以通过返回代码来判断哪个是哪个fork- 子进程获取 0,父进程获取子进程的 PID。当然,假设fork调用有效,这就是全部 - 如果没有,则不会创建子节点并且父节点会收到错误代码。
exec调用是一种基本上用新程序替换整个当前进程的方法。它将程序加载到当前进程空间并从入口点运行它。
因此,fork经常exec按顺序使用,以使新程序作为当前进程的子进程运行。当你尝试运行一个程序时,shell 通常会这样做find——shell fork,然后子进程将find程序加载到内存中,设置所有命令行参数、标准 I/O 等等。
find
但它们不需要一起使用。例如,如果程序同时包含父代码和子代码(您需要小心您所做的事情,每个实现都可能有限制),那么程序fork本身没有ing是完全可以接受的。exec这在守护进程中被大量使用(现在仍然如此),这些守护进程只是侦听 TCP 端口和fork自身的副本,以在父进程返回侦听时处理特定请求。
同样,知道自己已经完成并且只想运行另一个程序的程序不需要fork,exec然后wait为孩子。他们可以直接将孩子加载到他们的进程空间中。
wait
一些 UNIX 实现有一个优化fork,它使用他们所谓的写时复制。这是一种延迟复制进程空间的技巧,fork直到程序尝试更改该空间中的某些内容。这对于那些只使用fork而不是exec因为它们不必复制整个进程空间的程序很有用。
如果exec 被称为跟随fork(这是最常发生的情况),则会导致写入进程空间,然后将其复制给子进程。
请注意,有一个完整的exec调用系列(execl、execle等execve),但exec在上下文中,这里指的是它们中的任何一个。
execl
execle
execve
下图说明了使用 shell 使用命令列出目录的典型fork/exec操作:bash``ls
fork/exec
bash``ls
+--------+ | pid=7 | | ppid=4 | | bash | +--------+ | | calls fork V +--------+ +--------+ | pid=7 | forks | pid=22 | | ppid=4 | ----------> | ppid=7 | | bash | | bash | +--------+ +--------+ | | | waits for pid 22 | calls exec to run ls | V | +--------+ | | pid=22 | | | ppid=7 | | | ls | V +--------+ +--------+ | | pid=7 | | exits | ppid=4 | <---------------+ | bash | +--------+ | | continues V